package me.chnxan.core;

import java.math.BigDecimal;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;

import me.chnxan.util.DateUtil;
import me.chnxan.util.ReflectionUtil;
import me.chnxan.util.StringUtil;


/**
 * 与具体ORM实现无关的属性过滤条件封装类.
 * 
 * PropertyFilter主要记录页面中简单的搜索过滤条件,比Hibernate的Criterion要简单很多.
 * 可按项目扩展其他对比方式如大于、小于及其他数据类型如数字和日期.
 * 
 * @author calvin
 */
public class PropertyFilter {

	/**
	 * 多个属性间OR关系的分隔符.
	 */
	public static final String OR_SEPARATOR = "__";

	/**
	 * 属性比较类型.
	 */
	public enum MatchType {
		EQ, LIKE, LIKER, LT, GT, LE, GE, IN, NE;
	}

	/**
	 * 属性数据类型.
	 */
	public enum PropertyType {
		S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), B(Boolean.class), C(BigDecimal.class);

		private Class<?> clazz;

		PropertyType(Class<?> clazz) {
			this.clazz = clazz;
		}

		public Class<?> getValue() {
			return clazz;
		}
	}
	
	private String[] propertyNames = null;
	private Class<?> propertyType = null;
	private Object value = null;
	private MatchType matchType = MatchType.EQ;

	public PropertyFilter() {
	}
	


	/**
	 * @param filterName 比较属性字符串,含待比较的比较类型、属性值类型及属性列表. 
	 *                   eg. LIKES_NAME_OR_LOGIN_NAME
	 * @param value 待比较的值.
	 */
	public PropertyFilter(final String filterName, final Object value) {

		Object tmp = value;
		String matchTypeStr = StringUtils.substringBefore(filterName, "_");
		String matchTypeCode = StringUtils.substring(matchTypeStr, 0, matchTypeStr.length() - 1);
		String propertyTypeCode = StringUtils.substring(matchTypeStr, matchTypeStr.length() - 1, matchTypeStr.length());
		try {
			matchType = Enum.valueOf(MatchType.class, matchTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性比较类型.", e);
		}

		try {
			propertyType = Enum.valueOf(PropertyType.class, propertyTypeCode).getValue();
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性值类型.", e);
		}
		
		String propertyNameStr = StringUtils.substringAfter(filterName, "_");
		//propertyNameStr = propertyNameStr.replaceAll("&", ".");
		propertyNames = StringUtils.split(propertyNameStr, PropertyFilter.OR_SEPARATOR);

		Assert.isTrue(propertyNames.length > 0, "filter名称" + filterName + "没有按规则编写,无法得到属性名称.");
		
		//针对操作日期的处理
		if(matchTypeStr.equals("LED")){
			tmp = tmp + " 23:59:59";
		}
		
		//按entity property中的类型将字符串转化为实际类型.
		this.value = ReflectionUtil.convertValue(tmp, propertyType);
	}
	
	public PropertyFilter(final String propertyName, final Object value, final MatchType matchType){
		this.propertyNames = new String[]{propertyName};
		this.matchType = matchType;
		
		this.value = value;
	}
	
	public PropertyFilter(final String[] propertyNames, final Object value, final MatchType matchType){
		this.propertyNames = propertyNames;
		this.matchType = matchType;
		this.value = value;
	}
	
	//================================================================================
	
	
	//================================================================================

	/**
	 * 是否有多个属性.
	 */
	public boolean isMultiProperty() {
		return (propertyNames.length > 1);
	}
	
	public MatchType getMatchType() {
		return matchType;
	}

	public void setMatchType(final MatchType matchType) {
		this.matchType = matchType;
	}

	public Class<?> getPropertyType() {
		return propertyType;
	}

	public void setPropertyType(Class<?> propertyType) {
		this.propertyType = propertyType;
	}
	
	/**
	 * 获取比较属性名称列表.
	 */
	public String[] getPropertyNames() {
		return propertyNames;
	}

	/**
	 * 获取唯一的属性名称.
	 */
	public String getPropertyName() {
		if (propertyNames.length > 1)
			throw new IllegalArgumentException("There are not only one property");
		return propertyNames[0];
	}

	public Object getValue() {
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}
	
	public String toSql(){
		String sql = "";
		
		if(this.propertyType!=null && "java.util.Date".equals(getPropertyType().toString())){
			this.value = DateUtil.convert((Date)value);
		}
		
		if (MatchType.EQ.equals(matchType)) {
			if(value==null){
				sql = this.getPropertyName() + " is null";
			} else {
				sql = this.getPropertyName() + " = '" + this.getValue() + "'";
			}
		}
		else if (MatchType.LIKE.equals(matchType)) {
			sql = this.getPropertyName() + " like '%" + this.getValue() +"%'";
		}
		
		// great than
		else if (MatchType.GT.equals(matchType)) {
			sql = this.getPropertyName() + " > '" + this.getValue() + "'";
		}
		// little than
		else if (MatchType.LT.equals(matchType)) {
			sql = this.getPropertyName() + " < '" + this.getValue() + "'";
		}
		
		// great equal
		else if (MatchType.GE.equals(matchType)) {
			sql = this.getPropertyName() + " >= '" + this.getValue() + "'";
		}
		
		// little less and equal than 
		else if (MatchType.LE.equals(matchType)) {
			sql = this.getPropertyName() + " <= '" + this.getValue() + "'";
		}

		// not equal
		else if (MatchType.NE.equals(matchType)) {
			sql = this.getPropertyName() + " <> '" + this.getValue() + "'";
		}
		
		// in
		else if (MatchType.IN.equals(matchType)) {
			String[] values = value.toString().split(",");
			sql = this.getPropertyName() + " in (" + StringUtil.getInClause(values) + ")";
		}
		
		return sql;
	}
}
